﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MemoryUtil;

namespace PlayerScannerCore.Model
{
    /// <summary>
    /// A class whose purpose is to read item related information.
    /// </summary>
    internal class ItemReader
    {
        private readonly BaseAddress BASE_ITEMS_ARRAY_PTR = new BaseAddress(0x00a8d81c);
        private readonly BaseAddress BASE_NUM_ITEMS_ADDR = new BaseAddress(0x00a8d820);
        private readonly BaseAddress BASE_EXTRA_ITEM_INFO_ARRAY_PTR = new BaseAddress(0x00a8dc94);

        private const int ITEM_PTR_PREV = 0x0C; //Prev and next pointers could be used to traverse the item collection but they're not used right now.
        private const int ITEM_PTR_NEXT = 0x10; 
        private const int ITEM_OWNER = 0xE4;
        private const int ITEM_KILLS = 0xE8;
        private const int ITEM_CODE = 0xF2;
        private const int ITEM_IS_NORMAL = 0xF5;
        private const int ITEM_REGULAR_TEXT_POINTER = 0x100;
        private const int ITEM_MESETA_AMOUNT = 0x100;
        private const int ITEM_REGULAR_COUNT = 0x104;
        private const int ITEM_TECH_TYPE = 0x108;
        private const int ITEM_IS_EQUIPPED = 0x190;
        private const int ITEM_ARMOR_SLOTS = 0x1B8;
        private const int ITEM_ARMOR_DEF_BONUS = 0x1B9;
        private const int ITEM_ARMOR_EVP_BONUS = 0x1BA;
        private const int ITEM_MAG_IQ = 0x1BC;
        private const int ITEM_MAG_SYNC = 0x1BE;
        private const int ITEM_MAG_STATS = 0x1C0;
        private const int ITEM_MAG_PB_POSITIONS = 0x1C8;
        private const int ITEM_MAG_PBS = 0x1C9;
        private const int ITEM_STATS = 0x1C8;
        private const int ITEM_SHIELD_DEF_BONUS = 0x1E4;
        private const int ITEM_SHIELD_EVP_BONUS = 0x1E5;
        private const int ITEM_GRIND = 0x1F5;
        private const int ITEM_SPECIAL = 0x1F6;
        private const int ITEM_SRANK_SPECIAL = 0x240;

        private const int EXTRA_INFO_WEAPON= 0x00;
        private const int EXTRA_INFO_ARMOR = 0x04;
        private const int EXTRA_INFO_UNIT = 0x08;
        private const int EXTRA_INFO_REGULAR = 0x0c;
        private const int EXTRA_INFO_MAG = 0x10;

        private const int EXTRA_INFO_WEAPON_EQUIPPABLE_BY = 0x0c;
        private const int EXTRA_INFO_WEAPON_MIN_BASE_ATP = 0x0e;
        private const int EXTRA_INFO_WEAPON_MAX_BASE_ATP = 0x10;
        private const int EXTRA_INFO_WEAPON_ATP_REQ = 0x12; 
        private const int EXTRA_INFO_WEAPON_MST_REQ = 0x14;
        private const int EXTRA_INFO_WEAPON_ATA_REQ = 0x16;
        private const int EXTRA_INFO_WEAPON_MST = 0x18;
        private const int EXTRA_INFO_WEAPON_MAX_GRIND = 0x1a;
        private const int EXTRA_INFO_WEAPON_ATA = 0x1d;

        private const int EXTRA_INFO_ARMOR_SHIELD_DEF = 0x0C;
        private const int EXTRA_INFO_ARMOR_SHIELD_EVP = 0x0E;
        private const int EXTRA_INFO_ARMOR_SHIELD_EQUIPPABLE_BY = 0x012;
        private const int EXTRA_INFO_ARMOR_SHIELD_LVL_REQ = 0x014;
        private const int EXTRA_INFO_ARMOR_SHIELD_EFR = 0X015;
        private const int EXTRA_INFO_ARMOR_SHIELD_ETH = 0X016;
        private const int EXTRA_INFO_ARMOR_SHIELD_EIC = 0X017;
        private const int EXTRA_INFO_ARMOR_SHIELD_EDK = 0X018;
        private const int EXTRA_INFO_ARMOR_SHIELD_ELT = 0X019;

        private ProcessMemory _processMemory;
        private LabelReader _labelReader;

        internal ItemFactory ItemFactory { get { return Factory.Instance.ItemFactory; } }

        public ItemReader(ProcessMemory processMemory, LabelReader labelReader)
        {
            _processMemory = processMemory;
            _labelReader = labelReader;
        }

        public List<IItem> GetFloorItems()
        {
            var ret = new List<IItem>();

            //All items are stored in one giant array. This forces this code to look at each item
            //to determine whether or not it is the selected player's item.

            var numItems = _processMemory.ReadInt32(BASE_NUM_ITEMS_ADDR);
            var itemsArrayAddress = _processMemory.ReadUInt32(BASE_ITEMS_ARRAY_PTR);

            for (int i = 0; i < numItems; i++)
            {
                var currentItemAddress = _processMemory.ReadUInt32(itemsArrayAddress + (uint)(4 * i));
                if (_processMemory.ReadSByte(currentItemAddress + ItemReader.ITEM_OWNER) == -1)
                    ret.Add(ReadItem(currentItemAddress));
            }

            return ret;
        }

        public List<IItem> GetPlayerInventory(int playerIndex)
        {
            var ret = new List<IItem>();

            //All items are stored in one giant array. This forces this code to look at each item
            //to determine whether or not it is the selected player's item.

            var numItems = _processMemory.ReadInt32(BASE_NUM_ITEMS_ADDR);
            var itemsArrayAddress = _processMemory.ReadUInt32(BASE_ITEMS_ARRAY_PTR);

            for (int i = 0; i < numItems; i++)
            {
                var currentItemAddress = _processMemory.ReadUInt32(itemsArrayAddress + (uint)(4 * i));
                if (_processMemory.ReadSByte(currentItemAddress + ItemReader.ITEM_OWNER) == playerIndex)
                    ret.Add(ReadItem(currentItemAddress));
            }

            return ret;
        }

        private IItem ReadItem(uint itemAddress)
        {
            IItem ret = null;

            //First get the item code
            var itemCode = _processMemory.Read(itemAddress + ITEM_CODE, 3);

            //Using the code, the item's type can be determined
            switch (itemCode[0])
            {
            case 0x00:
                if ((itemCode[1] >= 0xA5 && itemCode[1] <= 0xA9) || (itemCode[1] >= 0x70 && itemCode[1] <= 0x88)) 
                    ret = ReadSWeapon(itemAddress, itemCode);
                else
                    ret = ReadWeapon(itemAddress, itemCode);
                break;
            case 0x01:
                switch (itemCode[1])
                {
                case 0x01:
                    ret = ReadArmor(itemAddress, itemCode);
                    break;
                case 0x02:
                    ret = ReadShield(itemAddress, itemCode);
                    break;
                case 0x03:
                    ret = ReadUnit(itemAddress, itemCode);
                    break;
                }
                break;
            case 0x02:
                ret = ReadMag(itemAddress, itemCode);
                break;
            case 0x03:
                switch (itemCode[1])
                {
                case 0x02:
                    ret = ReadTech(itemAddress, itemCode);
                    break;
                default:
                    ret = ReadRegular(itemAddress, itemCode);
                    break;
                }
                break;
            case 0x04:
                ret = ReadMeseta(itemAddress, itemCode);
                break;
            }

            if (ret == null)
            {
                ret = ItemFactory.CreateUnknownItem();
                ret.ItemName = new StringBuilder().Append("Unrecognized Item (").
                    Append(itemCode[0]).Append(" ").Append(itemCode[1]).
                    Append(" ").Append(itemCode[2]).Append(")").ToString();
                ret.Description = "Unknown item";
            }

            return ret;
        }

        private ISWeapon ReadSWeapon(uint itemAddress, byte[] itemCode)
        {
            var extraInfoAddress = _processMemory.ReadUInt32(new Pointer(
                BASE_EXTRA_ITEM_INFO_ARRAY_PTR,
                EXTRA_INFO_WEAPON,
                4 + (itemCode[1] * 8))) + (uint)(itemCode[2] * 0x2c);
            var textIndex = _processMemory.ReadInt32(extraInfoAddress);
            var customNameEncoded = _processMemory.Read(itemAddress + ITEM_STATS, 6);
            var customNameDecoded = new StringBuilder();

            //Decode the custom s-rank weapon's name
            for (int i = 0; i < 6; i+=2)
            {
                customNameEncoded[i] &= 0x7f;
                byte temp = customNameEncoded[i];
                temp >>= 2;
                temp += 0x40;
                //Skip the first character we get (PSO does this)
                if (temp != 0x40 && i != 0)
                    customNameDecoded.Append(Convert.ToChar(temp));
                temp = (byte)(((customNameEncoded[i] << 3) & 0x1F) | (customNameEncoded[i + 1] >> 5));
                temp += 0x40;
                if (temp != 0x40)
                    customNameDecoded.Append(Convert.ToChar(temp));
                temp = (byte)(customNameEncoded[i + 1] & 0x1F);
                temp += 0x40;
                if (temp != 0x40)
                    customNameDecoded.Append(Convert.ToChar(temp));
            }
            var special = _processMemory.ReadByte(itemAddress + ITEM_SRANK_SPECIAL);
            string specialName;
            switch (special)
            {
            case 1:
                specialName = "Jellen";
                break;
            case 2:
                specialName = "Zalure";
                break;
            default:
                specialName = _labelReader.GetText(TextType.ItemSpecial, special);
                break;
            }

            var ret = ItemFactory.CreateSWeapon();
            ret.Id = itemAddress;
            ret.IsEquipped = (_processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED) & 0x01) == 0x01;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.Grind = _processMemory.ReadByte(itemAddress + ITEM_GRIND);
            ret.MaxGrind = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_MAX_GRIND);
            ret.CustomName = customNameDecoded.ToString();
            ret.Special = specialName;
            ret.EquippableBy = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_EQUIPPABLE_BY);
            ret.MinAtpBoost = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_WEAPON_MIN_BASE_ATP);
            ret.MaxAtpBoost = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_WEAPON_MAX_BASE_ATP);
            ret.AtpRequired = _processMemory.ReadUInt16(extraInfoAddress + EXTRA_INFO_WEAPON_ATP_REQ);
            ret.MstRequired = _processMemory.ReadUInt16(extraInfoAddress + EXTRA_INFO_WEAPON_MST_REQ);
            ret.AtaRequired = _processMemory.ReadUInt16(extraInfoAddress + EXTRA_INFO_WEAPON_ATA_REQ);
            ret.MstBoost = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_MST);
            ret.AtaBoost = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_ATA);

            return ret;
        }

        private IWeapon ReadWeapon(uint itemAddress, byte[] itemCode)
        {
            var extraInfoAddress = _processMemory.ReadUInt32(new Pointer(
                BASE_EXTRA_ITEM_INFO_ARRAY_PTR,
                EXTRA_INFO_WEAPON,
                4 + (itemCode[1] * 8))) + (uint)(itemCode[2] * 0x2c);
            var textIndex = _processMemory.ReadInt32(extraInfoAddress);
            var stats = _processMemory.Read(itemAddress + ITEM_STATS, 6);
            int nativeBonus = 0, aBeastBonus = 0, machineBonus = 0, darkBonus = 0, hitBonus = 0;
            for (int i = 0; i < 6; i+=2)
            {
                var currentBonus = (sbyte)stats[i+1];
                switch (stats[i])
                {
                case 1:
                    nativeBonus = currentBonus;
                    break;
                case 2:
                    aBeastBonus = currentBonus;
                    break;
                case 3:
                    machineBonus = currentBonus;
                    break;
                case 4:
                    darkBonus = currentBonus;
                    break;
                case 5:
                    hitBonus = currentBonus;
                    break;
                }
            }

            var special = _processMemory.ReadByte(itemAddress + ITEM_SPECIAL);
            var name = _labelReader.GetText(TextType.ItemName, textIndex);
            var equip = _processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED);

            var ret = ItemFactory.CreateWeapon();
            ret.Id = itemAddress;
            ret.IsEquipped = (_processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED) & 0x01) == 0x01;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.NativeBonus = nativeBonus;
            ret.ABeastBonus = aBeastBonus;
            ret.MachineBonus = machineBonus;
            ret.DarkBonus = darkBonus;
            ret.HitBonus = hitBonus;
            ret.Grind = _processMemory.ReadByte(itemAddress + ITEM_GRIND);
            ret.MaxGrind = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_MAX_GRIND);
            ret.Kills = _processMemory.ReadUInt16(itemAddress + ITEM_KILLS);
            ret.Special = _labelReader.GetText(TextType.ItemSpecial, special);
            ret.IsUntekked = (special & 0x80) == 0x80;
            ret.IsBasic = _processMemory.ReadByte(itemAddress + ITEM_IS_NORMAL) != 0x04;
            ret.EquippableBy = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_EQUIPPABLE_BY);
            ret.MinAtpBoost = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_WEAPON_MIN_BASE_ATP);
            ret.MaxAtpBoost = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_WEAPON_MAX_BASE_ATP);
            ret.AtpRequired = _processMemory.ReadUInt16(extraInfoAddress + EXTRA_INFO_WEAPON_ATP_REQ);
            ret.MstRequired = _processMemory.ReadUInt16(extraInfoAddress + EXTRA_INFO_WEAPON_MST_REQ);
            ret.AtaRequired = _processMemory.ReadUInt16(extraInfoAddress + EXTRA_INFO_WEAPON_ATA_REQ);
            ret.MstBoost = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_MST);
            ret.AtaBoost = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_WEAPON_ATA);

            return ret;
        }

        private IArmor ReadArmor(uint itemAddress, byte[] itemCode)
        {
            var extraInfoAddress = _processMemory.ReadUInt32(new Pointer(
                BASE_EXTRA_ITEM_INFO_ARRAY_PTR,
                EXTRA_INFO_ARMOR,
                4 + ((itemCode[1]-1) * 8))) + (uint)(itemCode[2] * 0x20);
            var textIndex = _processMemory.ReadInt32(extraInfoAddress);

            var ret = ItemFactory.CreateArmor();
            ret.Id = itemAddress;
            ret.IsEquipped = (_processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED) & 0x01) == 0x01;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.Defense = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_DEF);
            ret.Evasion = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EVP);
            ret.DefenseBonus = _processMemory.ReadByte(itemAddress + ITEM_ARMOR_DEF_BONUS);
            ret.EvasionBonus = _processMemory.ReadByte(itemAddress + ITEM_ARMOR_EVP_BONUS);
            ret.EquippableBy = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EQUIPPABLE_BY);
            ret.LevelRequirement = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_LVL_REQ) + 1;
            ret.Efr = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EFR);
            ret.Eic = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EIC);
            ret.Eth = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_ETH);
            ret.Edk = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EDK);
            ret.Elt = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_ELT);
            ret.Slots = _processMemory.ReadByte(itemAddress + ITEM_ARMOR_SLOTS);

            return ret;
        }

        private IShield ReadShield(uint itemAddress, byte[] itemCode)
        {
            var extraInfoAddress = _processMemory.ReadUInt32(new Pointer(
                BASE_EXTRA_ITEM_INFO_ARRAY_PTR,
                EXTRA_INFO_ARMOR,
                4 + ((itemCode[1] - 1) * 8))) + (uint)(itemCode[2] * 0x20);
            var textIndex = _processMemory.ReadInt32(extraInfoAddress);

            var ret = ItemFactory.CreateShield();
            ret.Id = itemAddress;
            ret.IsEquipped = (_processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED) & 0x01) == 0x01;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.Defense = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_DEF);
            ret.Evasion = _processMemory.ReadInt16(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EVP);
            ret.DefenseBonus = _processMemory.ReadByte(itemAddress + ITEM_SHIELD_DEF_BONUS);
            ret.EvasionBonus = _processMemory.ReadByte(itemAddress + ITEM_SHIELD_EVP_BONUS);
            ret.EquippableBy = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EQUIPPABLE_BY);
            ret.LevelRequirement = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_LVL_REQ) + 1;
            ret.Efr = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EFR);
            ret.Eic = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EIC);
            ret.Eth = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_ETH);
            ret.Edk = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_EDK);
            ret.Elt = _processMemory.ReadByte(extraInfoAddress + EXTRA_INFO_ARMOR_SHIELD_ELT);
 
            return ret;
        }

        private IUnit ReadUnit(uint itemAddress, byte[] itemCode)
        {
            var extraInfoAddress = _processMemory.ReadUInt32(new Pointer(
                BASE_EXTRA_ITEM_INFO_ARRAY_PTR,
                EXTRA_INFO_UNIT,
                4)) + (uint)(itemCode[2] * 0x14);
            var textIndex = _processMemory.ReadInt32(extraInfoAddress);

            var ret = ItemFactory.CreateUnit();
            ret.Id = itemAddress;
            ret.IsEquipped = (_processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED) & 0x01) == 0x01;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.Kills = _processMemory.ReadUInt16(itemAddress + ITEM_KILLS);

            return ret;
        }

        /// <summary>
        /// Represents the left-most pb for every possible mag PB code. Each index represents one of
        /// the 256 possible mag PB code values and its value is the left-most PB for that PB code.
        /// </summary>
        private byte[] _magLeftPb = new byte[] {
            1,2,1,1,1,1,1,1,2,0,0,0,0,0,0,0,
            1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
            1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
            1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
            2,3,3,2,2,2,2,2,3,2,3,2,2,2,2,2,
            3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,
            2,2,1,1,1,1,1,1,2,2,1,1,1,1,1,1,
            2,2,1,1,1,1,1,1,2,2,1,1,1,1,1,1,
            3,4,4,4,3,3,3,3,4,3,4,4,3,3,3,3,
            4,4,3,4,3,3,3,3,4,4,4,2,2,2,2,2,
            3,3,3,2,2,2,2,2,3,3,3,2,2,2,2,2,
            3,3,3,2,2,2,2,2,3,3,3,2,2,2,2,2,
            4,5,5,5,5,4,4,4,5,4,5,5,5,4,4,4,
            5,5,4,5,5,4,4,4,5,5,5,4,5,4,4,4,
            5,5,5,5,5,3,3,3,4,4,4,4,3,3,3,3,
            4,4,4,4,3,3,3,3,4,4,4,4,3,3,3,3
        };

        private IMag ReadMag(uint itemAddress, byte[] itemCode)
        {
            var extraInfoAddress = _processMemory.ReadUInt32(new Pointer(
                BASE_EXTRA_ITEM_INFO_ARRAY_PTR,
                EXTRA_INFO_MAG,
                4)) + (uint)(itemCode[1] * 0x1c);
            var textIndex = _processMemory.ReadInt32(extraInfoAddress);
            var statsRaw = _processMemory.Read(itemAddress + ITEM_MAG_STATS, 8);
            int def, pow, dex, mst, nextDef, nextPow, nextDex, nextMst;
            def = pow = dex = mst = nextDef = nextPow = nextDex = nextMst = 0;

            for (int i = 0; i < 4; i++)
            {
                var currentCumulativeBonus = BitConverter.ToChar(statsRaw, i*2);
                var currentBonus = currentCumulativeBonus / 100;
                var currentNextBonus = currentCumulativeBonus % 100;
                switch (i)
                {
                case 0:
                    nextDef = currentNextBonus;
                    def = currentBonus;
                    break;
                case 1:
                    nextPow = currentNextBonus;
                    pow = currentBonus;
                    break;
                case 2:
                    nextDex = currentNextBonus;
                    dex = currentBonus;
                    break;
                case 3:
                    nextMst = currentNextBonus;
                    mst = currentBonus;
                    break;
                }
            }

            var activePbLocations = _processMemory.ReadByte(itemAddress + ITEM_MAG_PB_POSITIONS);
            var activePbs = _processMemory.ReadByte(itemAddress + ITEM_MAG_PBS);

            string leftPb, centerPb, rightPb;
            leftPb = centerPb = rightPb = "None";

            if ((activePbLocations & 0x01) == 0x01)
                centerPb = GetPbAsString(activePbs & 0x07);

            if ((activePbLocations & 0x02) == 0x02)
                rightPb = GetPbAsString((activePbs & 0x38) >> 3);

            if ((activePbLocations & 0x04) == 0x04)
                leftPb = GetPbAsString(_magLeftPb[activePbs]);

            var ret = ItemFactory.CreateMag();
            ret.Id = itemAddress;
            ret.IsEquipped = (_processMemory.ReadByte(itemAddress + ITEM_IS_EQUIPPED) & 0x01) == 0x01;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = "None";
            ret.Iq = _processMemory.ReadByte(itemAddress + ITEM_MAG_IQ);
            ret.Sync = _processMemory.ReadByte(itemAddress + ITEM_MAG_SYNC);
            ret.LeftPhotonBlast = leftPb;
            ret.CenterPhotonBlast = centerPb;
            ret.RightPhotonBlast = rightPb;
            ret.DefLevel = def;
            ret.PowLevel = pow;
            ret.DexLevel = dex;
            ret.MstLevel = mst;
            ret.NextDef = nextDef;
            ret.NextPow = nextPow;
            ret.NextDex = nextDex;
            ret.NextMst = nextMst;
            ret.Level = def + pow + dex + mst;

            return ret;
        }

        private string GetPbAsString(int code)
        {
            switch (code)
            {
            case 0:
                return "Farlla";
            case 1:
                return "Estlla";
            case 2:
                return "Golla";
            case 3:
                return "Pilla";
            case 4:
                return "Leilla";
            case 5:
                return "Twins";
            default:
                return "Invalid";
            }
        }

        private ITechDisk ReadTech(uint itemAddress, byte[] itemCode)
        {
            var textIndex = _processMemory.ReadByte(itemAddress + ITEM_TECH_TYPE);

            var ret = ItemFactory.CreateTechDisk();
            ret.Id = itemAddress;
            ret.ItemName = _labelReader.GetText(TextType.TechDisk, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.Level = itemCode[2] + 1;

            return ret;
        }

        private IRegularItem ReadRegular(uint itemAddress, byte[] itemCode)
        {
            var textIndex = _processMemory.ReadInt32(new Pointer(itemAddress + ITEM_REGULAR_TEXT_POINTER, 0));

            var ret = ItemFactory.CreateRegularItem();
            ret.Id = itemAddress;
            ret.ItemName = _labelReader.GetText(TextType.ItemName, textIndex);
            ret.Description = _labelReader.GetText(TextType.Description, textIndex);
            ret.Count = (int)(_processMemory.ReadInt32(itemAddress + ITEM_REGULAR_COUNT) ^ (itemAddress + ITEM_REGULAR_COUNT));

            return ret;
        }

        private IRegularItem ReadMeseta(uint itemAddress, byte[] itemCode)
        {
            //Name and description are set automatically
            var ret = ItemFactory.CreateMeseta();
            ret.ItemName = ret.Description = "Meseta";
            ret.Id = itemAddress;
            ret.Count = _processMemory.ReadInt32(itemAddress + ITEM_MESETA_AMOUNT);

            return ret;
        }
    }
}
